class LogicGate:

    def __init__(self, n):
        self.lable = n
        self.output = None

    def get_lable(self):
        return self.lable

    def get_output(self):
        self.output = self.perform_gate_logic()
        print(self.output)


class BinaryGate(LogicGate):

    def __init__(self, n):
        super().__init__(n)

        self.pinA = None
        self.pinB = None

    def get_pinA(self):
        if self.pinA is None:

            return int(input(f'Enter Pin A input for gate {self.get_lable()} -->'))
        else:
            return self.pinA.get_from().get_output()

    def get_pinB(self):
        return int(input(f'Enter Pin B input for gate {self.get_lable()} -->'))


class UnaryGate(LogicGate):

    def __init__(self, n):
        super().__init__(n)

        self.pin = None

    def get_pin(self):
        return int(input(f'Enter Pin input for gate {self.get_lable()} -->'))


class AndGate(BinaryGate):

    def __init__(self, n):
        super().__init__(n)

    def perform_gate_logic(self):
        a = self.get_pinA()
        b = self.get_pinB()
        if all([a, b]):
            return 1
        else:
            return 0


class OrGate(BinaryGate):

    def __init__(self, n):
        super().__init__(n)

    def perform_gate_logic(self):
        a = self.get_pinA()
        b = self.get_pinB()
        if any([a, b]):
            return 1
        else:
            return 0


class NotGate(UnaryGate):

    def __init__(self, n):
        super().__init__(n)

    def perform_gate_logic(self):
        a = self.get_pin()
        return not a


class Connector:

    def __init__(self, fgate, tgate):
        self.fromgate = fgate
        self.togate = tgate

        tgate.set_next_pin(self)

    def get_from(self):
        return self.fromgate

    def get_to(self):
        return self.togate


if __name__ == "__main__":
    g1 = AndGate('g1')
    g2 = AndGate('g2')
    g3 = OrGate('g3')
    g4 = NotGate('g4')


